namespace Skymate
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Abp.Dependency;

    using Castle.Core.Internal;

    using Skymate.Commands;
    using Skymate.Lyjt.Core;

    using SuperSocket.SocketBase;

    public class LightAppSession : AppSession<LightAppSession, SemaphoreRequestInfo>
    {
        private readonly IProtocolAnalysis _protocolAnalysis;

        public LightAppSession()
        {
            this._protocolAnalysis = IocManager.Instance.Resolve<IProtocolAnalysis>();
        }

        #region 链路信息

        /// <summary>
        /// 连接查询应答
        /// </summary>
        public void OnlineQueryResponse()
        {
            this.SendCommunicationData(
                ProtocolConst.OptionType.QueryResponse,
                ProtocolConst.ObjectType.Online);
        }

        /// <summary>
        /// 联机请求应答
        /// </summary>
        public void OnlineRequestResponse()
        {
            this.SendCommunicationData(
                ProtocolConst.OptionType.SetResponse,
                ProtocolConst.ObjectType.Online);
        }
        #endregion

        #region 基础信息
        /// <summary>
        /// 请求灯色
        /// </summary>
        public void RequestLightColor()
        {
            this.SendInfoData(ProtocolConst.OptionType.QueryRequest, ProtocolConst.ObjectType.RedState);
        }
        
        /// <summary>
        ///     请求时间
        /// </summary>
        public void RequestDatetime()
        {
            this.SendInfoData(ProtocolConst.OptionType.QueryRequest, ProtocolConst.ObjectType.Datetime);
        }

        /// <summary>
        /// 设置日期
        /// </summary>
        /// <param name="date">
        /// The date.
        /// </param>
        public void SetDate(DateTime date)
        {
            var start = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            var ms = date - start;

            this.SendInfoData(
                ProtocolConst.OptionType.SetRequest,
                ProtocolConst.ObjectType.Datetime,
                BitConverter.GetBytes((int)ms.TotalSeconds));
        }

        #endregion

        #region 特征参数

        /// <summary>
        /// 请求配时方案
        /// </summary>
        public void RequestTimingPlan()
        {
            this.SendLatentParameterData(
                ProtocolConst.OptionType.QueryRequest,
                ProtocolConst.ObjectType.SignalTimingScheme);
        }

        /// <summary>
        /// 请求方案调度
        /// </summary>
        public void RequestSchedSolution()
        {
            this.SendLatentParameterData(
                ProtocolConst.OptionType.QueryRequest,
                ProtocolConst.ObjectType.ProjectSchedulingPlan);
        }

        public void RequestLightGroup()
        {
            this.SendLatentParameterData(ProtocolConst.OptionType.QueryRequest, ProtocolConst.ObjectType.SignalLampGroup);
        }

        /// <summary>
        /// 请求相位信息
        /// </summary>
        public void RequestPhase()
        {
            this.SendLatentParameterData(ProtocolConst.OptionType.QueryRequest, ProtocolConst.ObjectType.Phase);
        }

        public void SetPhases(IEnumerable<Phase> phases)
        {
            if (phases == null)
            {
                return;
            }

            var buffer = new List<byte>();
            phases.ForEach(
                c =>
                    {
                        buffer.AddRange(c.ToBuffer());
                    });

            this.SetPhase(phases.Count(), buffer.ToArray());
        }

        /// <summary>
        /// 设置配时方案
        /// </summary>
        /// <param name="plnas"></param>
        public void SetTimplans(IEnumerable<TimingPlan> plnas)
        {
            List<byte> buffer = new List<byte>();
            foreach (TimingPlan item in plnas)
            {
                buffer.AddRange(item.ToBytes());
            }

            this.SetTimplan(plnas.Count(), buffer.ToArray());
        }

        public void SetSchedSolutions(IEnumerable<SchedSolution> solutions)
        {
            List<byte> buffer = new List<byte>();
            foreach (SchedSolution item in solutions)
            {
                buffer.AddRange(item.ToBytes());
            }

            this.SetSchedSolution(solutions.Count(), buffer.ToArray());
        }

        public void SetLightGroups(IEnumerable<LightGroup> groups)
        {
            List<byte> buffer = new List<byte>();

            groups.ForEach(c => buffer.AddRange(c.ToBytes()));

            this.SetLightGroup(groups.Count(), buffer.ToArray());
        }

        /// <summary>
        /// 设置灯组
        /// </summary>
        /// <param name="count">
        /// The count.
        /// </param>
        /// <param name="buffer">
        /// The buffer.
        /// </param>
        private void SetLightGroup(int count, byte[] buffer)
        {
            var list = buffer.ToList();
            list.Insert(0, (byte)count);
            this.SendLatentParameterData(
                ProtocolConst.OptionType.SetRequest,
                ProtocolConst.ObjectType.SignalLampGroup,
                list.ToArray());
        }

        private void SetTimplan(int count, byte[] buffer)
        {
            var list = buffer.ToList();
            list.Insert(0, (byte)count);
            this.SendLatentParameterData(
                ProtocolConst.OptionType.SetRequest,
                ProtocolConst.ObjectType.SignalTimingScheme,
                list.ToArray());
        }

        /// <summary>
        /// 设置相位信息
        /// </summary>
        /// <param name="count">
        /// The count.
        /// </param>
        /// <param name="buffer">
        /// The buffer.
        /// </param>
        private void SetPhase(int count, byte[] buffer)
        {
            var list = buffer.ToList();
            list.Insert(0, (byte)count);
            this.SendLatentParameterData(
                ProtocolConst.OptionType.SetRequest,
                ProtocolConst.ObjectType.Phase,
                list.ToArray());
        }

        private void SetSchedSolution(int count, byte[] buffer)
        {
            var list = buffer.ToList();
            list.Insert(0, (byte)count);
            this.SendLatentParameterData(
                ProtocolConst.OptionType.SetRequest,
                ProtocolConst.ObjectType.ProjectSchedulingPlan,
                list.ToArray());
        }

        #endregion

        private List<byte> BuildSendData(
            byte linkcode,
            byte optionType,
            byte objectType)
        {
            var sendData = new List<byte>
                               {
                                   ProtocolConst.Version,
                                   ProtocolConst.Computer,
                                   ProtocolConst.LightController,
                                   linkcode,
                                   ProtocolConst.ByteEmpty,
                                   ProtocolConst.ByteEmpty,
                                   ProtocolConst.ByteEmpty,
                                   optionType,
                                   objectType,
                                   0x01,
                                   0x01,
                                   0x01,
                                   0x01,
                                   0x01
                               };
            
            return sendData;
        }

        private void SendLatentParameterData(byte option, byte obj, byte[] data = null)
        {
            var sendList = this.BuildSendData(ProtocolConst.DataLink.LatentParameter, option, obj);
            if (data != null)
            {
                sendList.AddRange(data);
            }

            this.SendData(sendList.ToArray());
        }

        private void SendCommunicationData(byte option, byte obj, byte[] data = null)
        {
            var sendList = this.BuildSendData(ProtocolConst.DataLink.Communication, option, obj);
            if (data != null)
            {
                sendList.AddRange(data);
            }

            this.SendData(sendList.ToArray());
        }

        private void SendInfoData(byte option, byte obj, byte[] data = null)
        {
            var sendList = this.BuildSendData(ProtocolConst.DataLink.Info, option, obj);
            if (data != null)
            {
                sendList.AddRange(data);
            }
            
            this.SendData(sendList.ToArray());
        }
        
        /// <summary>
        /// 发送的是原数据
        /// 该函数负责加密和发送
        /// </summary>
        /// <param name="buffer">未加密的数据内容</param>
        private void SendData(byte[] buffer)
        {
            var targetBuffer = this._protocolAnalysis.EncryData(buffer);

            this.Send(targetBuffer, 0, targetBuffer.Length);
        }
    }
}